﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FizzBuzzGenerator
{
    /// <summary>
    /// Abstract class for fizz buzz and custom token generation. 
    /// </summary>
    public abstract class AbstractTokenGenerator
    {
        private readonly int _start;
        private readonly int _end;
        private readonly string _divisibleBy3Token;
        private readonly string _divisibleBy5Token;
        private readonly string _divisibleBy3And5Token;
        private readonly string _notDivisibleToken;

        /// <summary>
        /// This contructors only expects start and end numbers. It will set tokens to the default values (Fizz, Buzz, FizzBuzz). 
        /// </summary>
        /// <param name="start">Number to start</param>
        /// <param name="end">Number to end</param>
        protected AbstractTokenGenerator(int start, int end)
        {
            _start = start;
            _end = end;
            _divisibleBy3Token = "Fizz";
            _divisibleBy5Token = "Buzz";
            _divisibleBy3And5Token = "FizzBuzz";
            _notDivisibleToken = "";
        }

        /// <summary>
        /// Constructor with default behavior. 
        /// </summary>
        protected AbstractTokenGenerator()
        {
            _start = -3;
            _end = 15;
            _divisibleBy3Token = "Fizz";
            _divisibleBy5Token = "Buzz";
            _divisibleBy3And5Token = "FizzBuzz";
            _notDivisibleToken = "";
        }

        /// <summary>
        /// A full customization of the FizzBuzz Generator
        /// </summary>
        /// <param name="start">Number to start</param>
        /// <param name="end">Number to end</param>
        /// <param name="divisibleBy3Token">Custom token for divisible by 3 values. </param>
        /// <param name="divisibleBy5Token">Custom token for divisible by 5 values.</param>
        /// <param name="divisibleBy3And5Token">Custom token for divisible by 5 and 3 values.</param>
        /// <param name="notDivibleToken">Custom token for values not divisible by 3 or 5. </param>
        protected AbstractTokenGenerator(int start, int end, string divisibleBy3Token, string divisibleBy5Token, string divisibleBy3And5Token, string notDivibleToken)
        {
            _start = start;
            _end = end;
            _divisibleBy3Token = divisibleBy3Token;
            _divisibleBy5Token = divisibleBy5Token;
            _divisibleBy3And5Token = divisibleBy3And5Token;
            _notDivisibleToken = notDivibleToken;
        }

        /// <summary>
        /// Generates tokens based on the input numbers and display tokens in the class. 
        /// </summary>
        /// <returns>List of string with tokens.</returns>
        protected List<string> GenerateTokens()
        {
            var result = new List<string>();
         
            for (int i = _start; i <= _end; i++)
            {
                if (i == 0)
                {
                    result.Add(_notDivisibleToken);
                    continue;
                }
                if (IsDivisibleBy3And5(i))
                {
                    result.Add(_divisibleBy3And5Token);
                }
                else if (IsDivisibleBy3(i))
                {
                    result.Add(_divisibleBy3Token);
                }
                else if (IsDivisibleBy5(i))
                {
                    result.Add(_divisibleBy5Token);
                }
                else
                {
                    result.Add(_notDivisibleToken);
                }
            }
            return result;
        }

        /// <summary>
        /// Checks to see if the number passed is divisible by 3 and 5.
        /// </summary>
        /// <param name="number"></param>
        /// <returns>Returns true if the number is divisble by 3 and 5, else false.</returns>
        private bool IsDivisibleBy3And5(int number)
        {
            return number % 3 == 0 && number % 5 == 0;
        }

        /// <summary>
        /// Checks to see if the number passed is divisble by 5.
        /// </summary>
        /// <param name="number"></param>
        /// <returns>Returns true if the number is divisble by 5, else false.</returns>
        private bool IsDivisibleBy5(int number)
        {
            return number % 5 == 0;
        }

        /// <summary>
        /// Checks to see if the number passed is divisble by 3.
        /// </summary>
        /// <param name="number"></param>
        /// <returns>Returns true if the number is divisble by 3, else false.</returns>
        private bool IsDivisibleBy3(int number)
        {
            return number % 3 == 0;
        }

        /// <summary>
        /// Abstract method to implement different ouput methodologies e.g. (console, .txt etc). 
        /// </summary>
        public abstract void GenerateFizzBuzzOutput();

    }
}
